﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Linq.Expressions;
using System.Data.Entity;
using Core.Queries;
using Core.IRepository;
using Shared.Expressions;

namespace Repositories
{
    public class Repository<TId, TEntity> : IRepository<TId, TEntity>
        where TEntity : class
    {        
        private readonly DbContext Context;

        protected virtual DbSet<TEntity> Set
        {
            get
            {
                return Context.Set<TEntity>();
            }
        }

        public Repository(DbContext context)
        {
            this.Context = context;        
        }

        public virtual TEntity FindBy(TId id)
        {
            return Set.Find(id);
        }

        public virtual void Add(TEntity entity)
        {
            Set.Add(entity);
        }

        public virtual void Edit(TEntity entity)
        {
            Context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        }

        public void Delete(TEntity entity)
        {
            Set.Remove(entity);
        }

        public void Delete(Expression<Func<TEntity, bool>> predicate)
        {
            var list = Set.Where(predicate).ToList();

            foreach (TEntity entity in list)
            {
                Set.Remove(entity);
            }
        }

        public IList<TEntity> FindBy(Query<TEntity> query)
        {
            if (query.PagingEnabled && string.IsNullOrEmpty(query.SortColumn))
                throw new Exception("PagingEnabled enabled but SortColumn not defined");

            IQueryable<TEntity> q = Set.Where(query.ToExpression());

            if (query.PagingEnabled && !string.IsNullOrEmpty(query.SortColumn))
            {
                if (query.SortAscending)
                    q = q.OrderBy(query.SortColumn);
                else
                    q = q.OrderByDescending(query.SortColumn);

                q = q.Skip((query.PageNumber - 1) * query.PageSize).Take(query.PageSize);
            }

            return q.ToList();            
        }

        public int Count(Query<TEntity> query)
        {            
            if (Context != null)
                Context.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

            var count = Set.Where(query.ToExpression()).Count();

            if (Context != null)
                Context.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ COMMITTED;");

            return count;            
        }

        public IList<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate)
        {
            return Set.Where(predicate).ToList();
        }

        public IList<TEntity> FindAll()
        {
            return Set.ToList();
        }
    }   
}
